package com.zhaungjie.base.utils.ExcelUtil;

import com.zhaungjie.base.utils.ExcelUtil.spi.ExcelUtilReadHandler;
import com.zhaungjie.base.utils.ExcelUtil.spi.ExcelUtilWriteHandler;
import com.zhaungjie.base.utils.ExcelUtil.spi.impl.easy.ExcelUtilReadHandlerForEasy;
import com.zhaungjie.base.utils.ExcelUtil.spi.impl.easy.ExcelUtilWriteHandlerForEasy;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * Excel工具类
 *
 * @author zhuangjie
 * @date 2024/06/02
 */
public class ExcelUtil {


    /**
     * excel构建后操作
     *
     * @author zhuangjie
     * @date 2024/06/02
     */
    public static class ExcelBuildPostOperation {
        private final ExcelUtilWriteHandler excelUtilWriteHandler;

        public ExcelBuildPostOperation(ExcelUtilWriteHandler excelUtilWriteHandler) {
            this.excelUtilWriteHandler = excelUtilWriteHandler;
        }

        public ExcelBuildPostOperation responseDownload(OutputStream productOutputStream, HttpServletResponse response, String fileName) {
            // 判断文件名是否有后缀
            if (!fileName.contains(".")) {
                fileName = fileName + ".xlsx";
            }
            byte[] fileNameBytes = fileName.getBytes(StandardCharsets.UTF_8);
            fileName = new String(fileNameBytes, StandardCharsets.ISO_8859_1);
            response.setContentType("application/vnd.ms-excel");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName);
            excelUtilWriteHandler.outputExcel(productOutputStream);
            return this;
        }

        public ExcelBuildPostOperation responseDownload(OutputStream productOutputStream, String fileName) {
            // 设置响应下载的文件名
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletResponse response;
            if (servletRequestAttributes == null || (response = servletRequestAttributes.getResponse()) == null) {
                // 无法以静态方式获取到HttpServletResponse
                throw new RuntimeException("Unable to obtain HttpServletResponse statically！");
            }
            return responseDownload(productOutputStream, response, fileName);
        }

        /**
         * 保存到本地
         *
         * @param path 保存位置
         * @return 链式调用返回当前对象
         */
        public ExcelBuildPostOperation save(String path) {
            try (FileOutputStream os = new FileOutputStream(path)) {
                excelUtilWriteHandler.outputExcel(os);
            } catch (Exception e) {
                System.out.println("[ERROR] Excel保存到本地失败！" + e.getMessage());
            }
            return this;
        }

        public ExcelBuildPostOperation outputExcel(OutputStream productOutputStream) {
            excelUtilWriteHandler.outputExcel(productOutputStream);
            return this;
        }
    }

    /**
     * excel sheet 的数据定义
     *
     * @author zhuangjie
     * @date 2024/06/02
     */
    @Getter
    @Setter
    @AllArgsConstructor
    @NoArgsConstructor
    public static class ExcelSheet {
        private Integer sheetIndex;
        private String sheetName;
        private com.zhuangjie.boot.utils.excel.ExcelTable excelTable;
    }


    /**
     * excel操作
     *
     * @author zhuangjie
     * @date 2024/06/02
     */
    @Getter
    public static class ExcelWriteOperation {
        private List<ExcelSheet> sheetList;
        private int currentMaxIndex = -1;

        public ExcelWriteOperation writeSheet(Integer sheetIndex, String sheetName, com.zhuangjie.boot.utils.excel.ExcelTable excelTable) {
            if (excelTable == null) {
                return this;
            }
            if (sheetList == null) {
                sheetList = new ArrayList<>();
            }
            // 如果sheetIndex大于当前最大sheetIndex，则更新当前最大sheetIndex
            if (sheetIndex != null && sheetIndex > currentMaxIndex) {
                currentMaxIndex = sheetIndex;
            }
            // 如果sheetIndex为空，则自动生成
            if (sheetIndex == null) {
                sheetIndex = ++currentMaxIndex;
            }
            // 记录要写的sheet
            sheetList.add(new ExcelSheet(sheetIndex, sheetName, excelTable));
            return this;
        }

        /**
         * 当没有传入Index,根据维护的currentMaxIndex使用合适的index
         *
         * @param sheetName
         * @param excelTable
         * @return
         */
        public ExcelWriteOperation writeSheet(String sheetName, com.zhuangjie.boot.utils.excel.ExcelTable excelTable) {
            return writeSheet(null, sheetName, excelTable);
        }

        public ExcelBuildPostOperation build() {
            // - 主逻辑开始点 -
            // EasyExcel.write(outputStream, ImageDemoData.class).sheet().doWrite(list);
            return new ExcelBuildPostOperation(new ExcelUtilWriteHandlerForEasy(sheetList));
        }

        /**
         * 自定义excel处理逻辑（默认使用EasyExcel）
         *
         * @param excelUtilWriteHandler excel处理逻辑
         * @return excel构建后操作类ExcelBuildPostOperation
         */
        public ExcelBuildPostOperation build(ExcelUtilWriteHandler excelUtilWriteHandler) {
            // - 主逻辑开始点 -
            // EasyExcel.write(outputStream, ImageDemoData.class).sheet().doWrite(list);
            return new ExcelBuildPostOperation(excelUtilWriteHandler);
        }
    }

    public static ExcelWriteOperation createExcel() {
        return new ExcelWriteOperation();
    }

    // === 读模式 ===
    public interface ExcelReadEventListener<T> {
        default void onRow(T rowData) {
        }

        void onAllRowReadComplete(List<T> allRowData);
    }

    public static class ExcelReadOperation {
        private final ExcelUtilReadHandler excelUtilReadHandler;

        public ExcelReadOperation(ExcelUtilReadHandler excelUtilReadHandler) {
            this.excelUtilReadHandler = excelUtilReadHandler;
        }

        /**
         * 读取指定sheet
         *
         * @param sheetIndex             sheet索引
         * @param readClazz              表对应的class
         * @param excelReadEventListener excel阅读事件监听器
         * @return {@link ExcelReadOperation }
         */
        public <T> ExcelReadOperation readSheet(Integer sheetIndex, Class<T> readClazz, ExcelReadEventListener<T> excelReadEventListener) {
            excelUtilReadHandler.readSheet(sheetIndex, readClazz, excelReadEventListener);
            return this;
        }

        /**
         * 读取第一个sheet
         *
         * @param readClazz              表对应的class
         * @param excelReadEventListener excel阅读事件监听器
         * @return {@link ExcelReadOperation }
         */
        public <T> ExcelReadOperation readFirstSheet(Class<T> readClazz, ExcelReadEventListener<T> excelReadEventListener) {
            excelUtilReadHandler.readSheet(0, readClazz, excelReadEventListener);
            return this;
        }

        public<T> void readAllSheet(Class<T> readClazz, ExcelReadEventListener<T> excelReadEventListener) {
            excelUtilReadHandler.readAllSheet(readClazz, excelReadEventListener);
        }

        public void finish() {
            excelUtilReadHandler.finish();
        }

    }

    public static ExcelReadOperation loadExcel(String filePath) {
        try (FileInputStream fileInputStream = new FileInputStream(filePath)) {
            return loadExcel(fileInputStream);
        } catch (Exception e) {
            throw new RuntimeException("Excel读取失败！" + e.getMessage());
        }
    }

    public static ExcelReadOperation loadExcel(InputStream inputStream) {
        return loadExcel(new ExcelUtilReadHandlerForEasy(inputStream));
    }

    public static ExcelReadOperation loadExcel(ExcelUtilReadHandler excelUtilReadHandler) {
        return new ExcelReadOperation(excelUtilReadHandler);
    }


}

